/*
 * shared_rt_loader.hpp
 *
 *  Created on: 2023年10月17日
 *      Author: Dylan.Gao
 */

#ifndef _DM_OS_SHARED_RT_LOADER_HPP_
#define _DM_OS_SHARED_RT_LOADER_HPP_

#include <dm/os/shared_rt_mgr.hpp>
#include <dm/os/db/db.hpp>
#include <dm/os/db/resultset.hpp>
#include <dm/env/cfg.hpp>
#include <dm/scoped_ptr.hpp>
#include <dm/os/types.hpp>
#include <dm/string/stringlist.hpp>

namespace dm{
namespace os{

/**
 * 加载器类
 * 实现了通过数据库加载数据的功能
 * @tparam TInfo
 * @tparam TValue
 * @tparam SizeOfMasks
 * @tparam SizeOfName
 */
template<typename TInfo,typename TValue,typename TMask=dm::uint8,int SizeOfMasks=8,int SizeOfName=64>
class TCSharedRtLoader:public TCSharedRtMgr<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::buf_mgr_t::CLoader{
public:
	TCSharedRtLoader();
	virtual ~TCSharedRtLoader();

	/**
	 * 创建数据库表
	 * @return
	 */
	bool createTable();

	bool setAll();
	bool setId( const id_t& id );
	bool setName( const char* name );

	bool addALoaded( dm::os::TSSharedRtRecord<TInfo,TValue,id_t>& record );

	bool filterLoaded();

	bool next();
	bool loadRecord( dm::os::TSSharedRtRecord<TInfo,TValue,id_t>& record );
protected:
	virtual const char* dbEngine( const dm::env::CCfg& cfg )const = 0;
	virtual const char* dbName( const dm::env::CCfg& cfg )const = 0;
	virtual const char* dbHost( const dm::env::CCfg& cfg )const = 0;
	virtual short dbPort( const dm::env::CCfg& cfg )const = 0;
	virtual const char* dbUser( const dm::env::CCfg& cfg )const = 0;
	virtual const char* dbPasswd( const dm::env::CCfg& cfg )const = 0;

	virtual const char* sqlCreateSqlite3()const=0;
	virtual const char* sqlCreateMysql()const=0;
	virtual const char* sqlCreatePg()const=0;
	virtual const char* sqlCreateHgdb()const=0;

	virtual const char* sqlSelect()const=0;

	virtual void loadRecord( dm::os::CResultSet& rs,TInfo& info,TValue& value )=0;

private:
	dm::TScopedPtr<dm::os::CDb> m_db;
	dm::TScopedPtr<dm::os::CResultSet> m_resultSet;

	std::vector<id_t> m_loadedRecordsId;

	dm::CTimeStamp m_ts;
	dm::CRunTimeStamp m_rts;
};

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::TCSharedRtLoader():m_db(),m_resultSet(){
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::~TCSharedRtLoader(){
	m_resultSet = nullptr;
	m_db = nullptr;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::createTable(){
	if( !m_db ){
		dm::env::CCfg& cfg = dm::env::CCfg::ins();
		m_db = dm::os::createDb(dbName(cfg),dbHost(cfg),dbEngine(cfg),dbPort(cfg));
		if( !m_db->connect(dbUser(cfg),dbPasswd(cfg)) )
			return false;
	}

	switch( m_db->dbType() ){
	case dm::os::CDb::DtSqlite:
		return m_db->exec(sqlCreateSqlite3());
	case dm::os::CDb::DtPg:
		return m_db->exec(sqlCreatePg());
	case dm::os::CDb::DtHgdb:
		return m_db->exec(sqlCreateHgdb());
	case dm::os::CDb::DtMysql:
		return m_db->exec(sqlCreateMysql());
	default:
		return false;
	}
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::setAll(){
	if( !m_db ){
		dm::env::CCfg& cfg = dm::env::CCfg::ins();
		m_db = dm::os::createDb(dbName(cfg),dbHost(cfg),dbEngine(cfg),dbPort(cfg));
		if( !m_db->connect(dbUser(cfg),dbPasswd(cfg)) )
			return false;
	}

	dm::string::CStringList stringList;
	stringList.append(sqlSelect());
	stringList.append(" order by id");
	m_resultSet = m_db->query(stringList.toString().c_str());
	if( !m_resultSet )
		return false;

	m_ts = dm::CTimeStamp::cur();
	m_rts = dm::CRunTimeStamp::cur();

	return true;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::setId( const id_t& id ){
	if( !m_db ){
		dm::env::CCfg& cfg = dm::env::CCfg::ins();
		m_db = dm::os::createDb(dbName(cfg),dbHost(cfg),dbEngine(cfg),dbPort(cfg));
		if( !m_db->connect(dbUser(cfg),dbPasswd(cfg)) )
			return false;
	}

	char sql[128];
	std::sprintf(sql,"%s where id=%d",sqlSelect(),id);

	m_resultSet = m_db->query(sql);
	if( !m_resultSet )
		return false;

	m_ts = dm::CTimeStamp::cur();
	m_rts = dm::CRunTimeStamp::cur();

	return true;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::setName( const char* name ){
	if( !m_db ){
		dm::env::CCfg& cfg = dm::env::CCfg::ins();
		m_db = dm::os::createDb(dbName(cfg),dbHost(cfg),dbEngine(cfg),dbPort(cfg));
		if( !m_db->connect(dbUser(cfg),dbPasswd(cfg)) )
			return false;
	}

	char sql[128];
	std::sprintf(sql,"%s where name='%s'",sqlSelect(),name);

	m_resultSet = m_db->query(sql);
	if( !m_resultSet )
		return false;

	m_ts = dm::CTimeStamp::cur();
	m_rts = dm::CRunTimeStamp::cur();

	return true;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::addALoaded( dm::os::TSSharedRtRecord<TInfo,TValue,id_t>& r ){
	id_t id;
	typename::dm::os::TSSharedRtRecord<TInfo,TValue,id_t>::CIdReader idReader;
	if( !r.get(&id, idReader) )
		return false;

	m_loadedRecordsId.push_back(id);

	return true;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::filterLoaded(){
	if( m_loadedRecordsId.size()==0 )
		return setAll();

	if( !m_db ){
		dm::env::CCfg& cfg = dm::env::CCfg::ins();
		m_db = dm::os::createDb(dbName(cfg),dbHost(cfg),dbEngine(cfg),dbPort(cfg));
		if( !m_db->connect(dbUser(cfg),dbPasswd(cfg)) )
			return false;
	}

	dm::string::CStringList sql;
	char ids[64];
	sql.append(sqlSelect());
	sql.append(" where id not in (");
	for( int i=0;i<m_loadedRecordsId.size();++i ){
		if( i==0 )
			std::sprintf(ids,"%ld",m_loadedRecordsId[i]);
		else
			std::sprintf(ids,",%ld",m_loadedRecordsId[i]);

		sql.append(ids);
	}
	sql.append(") order by id");

	m_resultSet = m_db->query(sql.toString().c_str());
	if( !m_resultSet )
		return false;

	m_ts = dm::CTimeStamp::cur();
	m_rts = dm::CRunTimeStamp::cur();

	return true;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::next(){
	if( m_resultSet )
		return m_resultSet->next();

	return false;
}

template<typename TInfo,typename TValue,typename TMask,int SizeOfMasks,int SizeOfName>
bool TCSharedRtLoader<TInfo,TValue,TMask,SizeOfMasks,SizeOfName>::loadRecord( dm::os::TSSharedRtRecord<TInfo,TValue,id_t>& record ){
	if( !m_resultSet )
		return false;

	TInfo info;
	TValue value;

	loadRecord(*m_resultSet, info, value);

	typename::dm::os::TSSharedRtRecord<TInfo,TValue,id_t>::CNormalLoader loader(&info,&value);

	return record.reload(loader, m_ts, m_rts);
}

}
}

#endif /* _DM_OS_SHARED_RT_LOADER_HPP_ */
